package com.gxa.autoparts.portal.service.impl;

import com.gxa.autoparts.core.common.ExceptionCode;
import com.gxa.autoparts.core.exception.BusinessException;
import com.gxa.autoparts.core.pojo.SecondKill;
import com.gxa.autoparts.portal.mapper.GoodsMapper;
import com.gxa.autoparts.portal.mapper.SecondKillMapper;
import com.gxa.autoparts.portal.service.SecondKillService;
import jdk.nashorn.internal.ir.CallNode;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class SecondKillServiceImpl implements SecondKillService, InitializingBean {


    @Autowired
    private RedisTemplate<String, Object> redisTemplate = null;

    /**
     * 记录商品标记
     */

    private HashMap<Long, Boolean> localMap = new HashMap<>();

    @Autowired
    SecondKillMapper secondKillMapper = null;

    private static final String SKU_CATCH = "goodsCatch:";
    private static final String KILL_Time = "goodsSecondKillTime:";
    private static final String KILL_SUCCESS = "secondKillUser:";


    private ValueOperations<String, Object> stringOps = null;

    @Override
    public Boolean secondKill(Long userId, Long skuId) throws BusinessException, ParseException {

        if (!isSecondKillTime(skuId)) {
            throw new BusinessException(ExceptionCode.NOT_TO_Time);
        }
        Boolean isNull = localMap.get(skuId);
        if (isNull) {
            //利用库存标记判断库存，为true时库存不足
            throw new BusinessException(ExceptionCode.SECONDKILL_FAILED);
        }
        //库存预减
        Long decrement = stringOps.decrement(SKU_CATCH + skuId);
        if (decrement < 0) {
            //库存<0，则说明本次请求已经没有库存，增加标记，抛出异常
            localMap.put(skuId, true);
            throw new BusinessException(ExceptionCode.SECONDKILL_FAILED);
        }
        //判断是否重复秒杀
        Boolean aBoolean = redisTemplate.hasKey(KILL_SUCCESS + skuId + userId);
        if (aBoolean) {
            throw new BusinessException(ExceptionCode.REPEAT_SUBMISSION);
        }
        //满足条件，进行秒杀
        //生成特征值，证明为统一用户上锁和解锁
        String uuidValue = UUID.randomUUID().toString().replace("-", "");
        //使用redis的NX机制实现分布式锁
        Boolean lockSuccess = stringOps.setIfAbsent(SKU_CATCH + skuId, uuidValue, 15, TimeUnit.SECONDS);
        if (!lockSuccess) {
            throw new BusinessException(ExceptionCode.LOCK_FAILED);
        }

        //解锁:通过删除操作,让lock变为不存在的状态
        if (uuidValue.equals(stringOps.get(SKU_CATCH + skuId))) {
            //记录秒杀成功的用户，避免重复秒杀
            stringOps.set(KILL_SUCCESS + skuId + userId, uuidValue);

            redisTemplate.delete(SKU_CATCH + skuId);
            //这里需要向消息队列发送消息创建订单


        }
        return true;

    }

    @Override
    public Boolean isSecondKillTime(Long skuId) throws ParseException {
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        StringBuffer stringBuffer = new StringBuffer(timestamp.toString().split(" ")[0]);
        ListOperations<String, Object> stringObjectListOperations = redisTemplate.opsForList();
        List<Object> range = stringObjectListOperations.range("goodsId:" + skuId, 0, -1);
        for (Object o : range) {
            stringBuffer.append(" ").append(o.toString());
            DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            format.setLenient(false);
            long time = format.parse(stringBuffer.toString()).getTime();
            Timestamp start = new Timestamp(time);
            Timestamp end = new Timestamp(time + 60L * 60 * 1000 * 1);
            if (timestamp.after(start) && timestamp.before(end)) {
                return true;
            }
        }
        return false;

    }

    @Override
    public void set() throws ParseException {

        ListOperations<String, Object> listOps = redisTemplate.opsForList();
        listOps.leftPush(KILL_Time + 1, "10:00:00");
        listOps.leftPush(KILL_Time + 1, "12:00:00");

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        stringOps = redisTemplate.opsForValue();
        List<SecondKill> secondKills = secondKillMapper.selectAll();

        for (SecondKill secondKill : secondKills) {
            //商品预热，降低数据库的压力
            stringOps.set(SKU_CATCH + secondKill.getGoodsId(), secondKill.getKillNumber());
            //商品标记，降低redis压力
            localMap.put(secondKill.getGoodsId(), false);
        }

    }
}
